home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / glchess / main.py < prev    next >
Text File  |  2009-09-22  |  25KB  |  747 lines

  1. # -*- coding: utf-8 -*-
  2. """
  3. """
  4.  
  5. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  6. __license__ = 'GNU General Public License Version 2'
  7. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  8.  
  9. __all__ = ['Application']
  10.  
  11. import sys
  12. import os
  13. import errno
  14. from gettext import gettext as _
  15. import traceback
  16. import time
  17.  
  18. import config
  19. import ui
  20. import gtkui
  21. import game
  22. import player
  23. import chess.board
  24. import chess.lan
  25. import chess.pgn
  26. import ai
  27. import network
  28. import display
  29. import history
  30.  
  31. from defaults import *
  32.        
  33. class PlayerTimer(ui.TimerFeedback):
  34.     """
  35.     """
  36.  
  37.     def __init__(self, game, colour, duration):
  38.         self.game = game
  39.         self.colour = colour
  40.         self.duration = duration
  41.         self.controller = game.application.ui.controller.addTimer(self, duration)
  42.     
  43.     def onTick(self, t):
  44.         """Called by ui.TimerFeedback"""
  45.         if self.colour is chess.board.WHITE:
  46.             self.game.view.controller.setWhiteTime(self.duration, t)
  47.         else:
  48.             self.game.view.controller.setBlackTime(self.duration, t)
  49.  
  50.     def onExpired(self):
  51.         """Called by ui.TimerFeedback"""
  52.         if self.colour is chess.board.WHITE:
  53.             self.game.getWhite().outOfTime()
  54.         else:
  55.             self.game.getBlack().outOfTime()
  56.  
  57. class ChessGame(game.ChessGame):
  58.     """
  59.     """
  60.  
  61.     # Mapping between piece names and promotion types
  62.     __promotionMapping = {'queen': chess.board.QUEEN, 'knight': chess.board.KNIGHT, 'bishop': chess.board.BISHOP, 'rook': chess.board.ROOK}
  63.  
  64.     def __init__(self, application, name):
  65.         """Constructor for a chess game.
  66.         
  67.         'application' is a reference to the glChess application.
  68.         'name' is the name of the game (string).
  69.         """
  70.         self.application = application
  71.         self.name = name
  72.         self.__aiPlayers = []
  73.         
  74.         # TEMP
  75.         self.duration = 0
  76.         self.wT = None
  77.         self.bT = None
  78.         
  79.         self.fileName    = None
  80.         self.inHistory   = False
  81.         self.needsSaving = False
  82.         
  83.         # Call parent constructor
  84.         self.view = None
  85.         game.ChessGame.__init__(self)
  86.  
  87.         self.view = display.View(self)
  88.         self.view.controller = application.ui.controller.setView(name, self.view)
  89.         self.view.updateRotation(animate = False)
  90.  
  91.         self.view.showMoveHints(config.get('show_move_hints') is True)
  92.         self.view.showBoardNumbering(config.get('show_numbering') is True)
  93.         self.view.showSmooth(config.get('show_3d_smooth') is True)        
  94.         
  95.         # Watch for piece moves with a player
  96.         self.__movePlayer = player.MovePlayer(self)
  97.         self.addSpectator(self.__movePlayer)
  98.         
  99.         self.date = time.strftime('%Y.%m.%d')
  100.  
  101.     def addAIPlayer(self, name, profile, level):
  102.         """Create an AI player.
  103.         
  104.         'name' is the name of the player to create (string).
  105.         'profile' is the the AI profile to use (ai.Profile).
  106.         'level' is the difficulty level to use (string).
  107.         
  108.         Returns an AI player to use (game.ChessPlayer).
  109.         """
  110.         # Translators: Description of an AI player used in log window. %(name)s is replaced with
  111.         # the name of the AI player. %(game)s is replaced with the name of the game the AI player
  112.         # is in.
  113.         description = _("'%(name)s' in '%(game)s'") % {'name': name, 'game': self.name}
  114.         p = player.AIPlayer(self.application, name, profile, level, description)
  115.         self.__aiPlayers.append(p)
  116.         self.application.watchAIPlayer(p)
  117.         return p
  118.  
  119.     def addHumanPlayer(self, name):
  120.         """Create a human player.
  121.         
  122.         'name' is the name of the player to create.
  123.         
  124.         Returns a human player to use (game.ChessPlayer).
  125.         """
  126.         return player.HumanPlayer(self, name)
  127.  
  128.     def setTimer(self, duration, whiteTime, blackTime):
  129.         self.duration = duration
  130.         if duration <= 0:
  131.             return
  132.  
  133.         self.view.controller.setWhiteTime(duration, whiteTime)
  134.         self.view.controller.setBlackTime(duration, blackTime)
  135.  
  136.         self.wT = PlayerTimer(self, chess.board.WHITE, whiteTime)
  137.         self.bT = PlayerTimer(self, chess.board.BLACK, blackTime)
  138.         self.wT.controller.run()
  139.         
  140.         self.setTimers(self.wT, self.bT)
  141.         
  142.     def getHumanPlayer(self):
  143.         """Get the human player.
  144.         
  145.         Returns the human player (HumanPlayer) or None if no human players.
  146.         If both players are human the current player is returned.
  147.         """
  148.         c = self.getCurrentPlayer()
  149.         if isinstance(c, player.HumanPlayer):
  150.             return c
  151.         white = self.getWhite()
  152.         black = self.getWhite()
  153.         if c is white:
  154.             opponent = black
  155.         else:
  156.             opponent = white
  157.         if isinstance(opponent, player.HumanPlayer):
  158.             return opponent
  159.         return None
  160.  
  161.     def currentPlayerIsHuman(self):
  162.         """Test if the player to move is human.
  163.  
  164.         Returns True if the current player is human and able to move.
  165.         """
  166.         p = self.getCurrentPlayer()
  167.         return isinstance(p, player.HumanPlayer) and p.isReadyToMove()
  168.  
  169.     def squareIsFriendly(self, coord):
  170.         """
  171.         """
  172.         owner = self.getSquareOwner(coord)
  173.         if owner is None:
  174.             return False
  175.         return owner is self.getCurrentPlayer()
  176.         
  177.     def moveHuman(self, start, end):
  178.         """
  179.         """
  180.         assert(self.currentPlayerIsHuman())
  181.         p = self.getCurrentPlayer()
  182.         if p is self.getWhite():
  183.             colour = chess.board.WHITE
  184.         else:
  185.             colour = chess.board.BLACK
  186.  
  187.         # Use configured promotion type
  188.         try:
  189.             promotionType = self.__promotionMapping[config.get('promotion_type')]
  190.         except KeyError:
  191.             promotionType = chess.board.QUEEN
  192.  
  193.         # Make the move
  194.         move = chess.lan.encode(colour, start, end, promotionType = promotionType)
  195.         p.move(move)
  196.  
  197.         # Notify move
  198.         self.view.controller.setAttention(False)
  199.  
  200.     def toPGN(self, pgnGame):
  201.         """Write the properties of this game into a PGN game.
  202.         
  203.         'pgnGame' is the game to write into (pgn.PGNGame). All the tags should be unset.
  204.         """
  205.         white = self.getWhite()
  206.         black = self.getBlack()
  207.         
  208.         pgnGame.setTag(chess.pgn.TAG_EVENT, self.name)
  209.         pgnGame.setTag(chess.pgn.TAG_WHITE, white.getName())
  210.         pgnGame.setTag(chess.pgn.TAG_BLACK, black.getName())
  211.         pgnGame.setTag(chess.pgn.TAG_DATE, self.date)
  212.  
  213.         results = {game.RESULT_WHITE_WINS: chess.pgn.RESULT_WHITE_WIN,
  214.                    game.RESULT_BLACK_WINS: chess.pgn.RESULT_BLACK_WIN,
  215.                    game.RESULT_DRAW:       chess.pgn.RESULT_DRAW}
  216.         try:
  217.             value = results[self.result]
  218.         except KeyError:
  219.             pass
  220.         else:
  221.             pgnGame.setTag(chess.pgn.TAG_RESULT, value)
  222.  
  223.         rules = {game.RULE_ABANDONMENT: chess.pgn.TERMINATE_ABANDONED,
  224.                  game.RULE_TIMEOUT:     chess.pgn.TERMINATE_TIME_FORFEIT,
  225.                  game.RULE_DEATH:       chess.pgn.TERMINATE_DEATH}
  226.         try:
  227.             value = rules[self.rule]
  228.         except KeyError:
  229.             pass
  230.         else:
  231.             pgnGame.setTag(chess.pgn.TAG_TERMINATION, value)
  232.  
  233.         if self.duration > 0:
  234.             pgnGame.setTag(chess.pgn.TAG_TIME_CONTROL, str(self.duration))
  235.         if self.wT is not None:
  236.             pgnGame.setTag('WhiteTime', str(self.wT.controller.getRemaining()))
  237.         if self.bT is not None:
  238.             pgnGame.setTag('BlackTime', str(self.bT.controller.getRemaining()))
  239.  
  240.         # FIXME: AI levels
  241.         if isinstance(white, ai.Player):
  242.             (profile, level) = white.getProfile()
  243.             pgnGame.setTag('WhiteAI', profile)
  244.             pgnGame.setTag('WhiteLevel', level)
  245.         if isinstance(black, ai.Player):
  246.             (profile, level) = black.getProfile()
  247.             pgnGame.setTag('BlackAI', profile)
  248.             pgnGame.setTag('BlackLevel', level)
  249.  
  250.         moves = self.getMoves()
  251.         for m in moves:
  252.             pgnMove = chess.pgn.PGNMove()
  253.             pgnMove.move = m.sanMove
  254.             pgnMove.nag = m.nag
  255.             pgnMove.comment = m.comment
  256.             pgnGame.addMove(pgnMove)
  257.  
  258.     def animate(self, timeStep):
  259.         """
  260.         """
  261.         return self.view.scene.controller.animate(timeStep)
  262.     
  263.     def endMove(self, p):
  264.         game.ChessGame.endMove(self, p)
  265.         self.view.updateRotation()
  266.  
  267.     def remove(self):
  268.         """Remove this game"""
  269.         # Remove AI player windows
  270.         for p in self.__aiPlayers:
  271.             p.window.close()
  272.             self.application.unwatchAIPlayer(p)
  273.  
  274.         # Stop the game
  275.         self.abort()
  276.         
  277.         # Remove the game from the UI
  278.         self.application._removeGame(self)
  279.         self.view.controller.close()
  280.  
  281.     def setNeedsSaving(self, needsSaving):
  282.         """
  283.         """
  284.         # Autosaved games don't need saving
  285.         if self.inHistory:
  286.             needsSaving = False
  287.  
  288.         if self.needsSaving == needsSaving:
  289.             return
  290.         self.needsSaving = needsSaving
  291.         self.view.controller.setNeedsSaving(needsSaving)
  292.  
  293.     def save(self):
  294.         """Save this game"""
  295.         pgnGame = chess.pgn.PGNGame()
  296.         self.toPGN(pgnGame)
  297.         if self.inHistory:
  298.             # Don't bother if haven't made any significant moves
  299.             if len(self.getMoves()) < 2:
  300.                 return
  301.             self.application.history.save(pgnGame, self.fileName)
  302.         else:
  303.             try:
  304.                 f = file(self.fileName, 'w')
  305.                 lines = pgnGame.getLines()
  306.                 for line in lines:
  307.                     f.write(line + '\n')
  308.                 f.write('\n')
  309.                 f.close()
  310.             except IOError, e:
  311.                 return e.strerror
  312.  
  313.         self.setNeedsSaving(False)
  314.         self.application.logger.addLine('Saved game %s to %s' % (repr(self.name), self.fileName))
  315.         
  316. class UI(ui.UIFeedback):
  317.     """
  318.     """
  319.  
  320.     def __init__(self, application):
  321.         """
  322.         """
  323.         self.controller = gtkui.GtkUI(self)
  324.         self.application = application
  325.         
  326.         self.splashscreen = display.Splashscreen(self)
  327.         self.splashscreen.controller = self.controller.setView('', self.splashscreen, isPlayable = False)
  328.  
  329.         self.ggzConfig = network.GGZConfig()
  330.         dialog = network.GGZNetworkDialog(self)
  331.         self.networkDialog = dialog.controller = self.controller.addNetworkDialog(dialog)
  332.         for server in self.ggzConfig.getServers():
  333.             dialog.controller.addProfile(server, server.name)
  334.  
  335.     def onAnimate(self, timeStep):
  336.         """Called by ui.UIFeedback"""
  337.         return self.application.animate(timeStep)
  338.     
  339.     def onReadFileDescriptor(self, fd):
  340.         """Called by ui.UIFeedback"""
  341.         try:
  342.             handler = self.application.ioHandlers[fd]
  343.         except KeyError:
  344.             return False
  345.         else:
  346.             result = handler.read()
  347.             if result is False:
  348.                 self.application.ioHandlers.pop(fd)
  349.             return result
  350.  
  351.     def onWriteFileDescriptor(self, fd):
  352.         """Called by ui.UIFeedback"""
  353.         try:
  354.             handler = self.application.ioHandlers[fd]
  355.         except KeyError:
  356.             return False
  357.         else:
  358.             result = handler.write()
  359.             if result is False:
  360.                 self.application.ioHandlers.pop(fd)
  361.             return result
  362.  
  363.     def onGameStart(self, game):
  364.         """Called by ui.UIFeedback"""
  365.         if game.white.type == '':
  366.             w = None
  367.         else:
  368.             w = (game.white.type, game.white.level)
  369.         if game.black.type == '':
  370.             b = None
  371.         else:
  372.             b = (game.black.type, game.black.level)
  373.         g = self.application.addLocalGame(game.name, game.white.name, w, game.black.name, b)
  374.         if g is None:
  375.             return
  376.         g.inHistory = True
  377.         self.application.logger.addLine('Starting game %s between %s (%s) and %s (%s). (%i moves)' % \
  378.                                         (game.name,
  379.                                          game.white.name, str(game.white.type),
  380.                                          game.black.name, str(game.black.type), len(game.moves)))
  381.  
  382.         g.setTimer(game.duration, game.duration, game.duration)
  383.         g.start(game.moves)
  384.         
  385.     def loadGame(self, path, configure):
  386.         """Called by ui.UI"""
  387.         try:
  388.             p = chess.pgn.PGN(path, 1)
  389.         except chess.pgn.Error, e:
  390.             return str(e)
  391.         except IOError, e:
  392.             return e.strerror
  393.         
  394.         # Use the first game
  395.         self.application.addPGNGame(p[0], path, configure)
  396.         
  397.         return None
  398.  
  399.     def onNewNetworkGame(self):
  400.         """Called by ui.UIFeedback"""
  401.         self.networkDialog.setVisible(True)
  402.         
  403.     def onQuit(self):
  404.         """Called by ui.UIFeedback"""
  405.         self.application.quit()
  406.  
  407. class Application:
  408.     """
  409.     """
  410.  
  411.     def __init__(self):
  412.         """Constructor for glChess application"""
  413.         self.__aiProfiles = {}       # The AI types
  414.         self.ioHandlers = {}         # Objects with IO keyed by file descriptor
  415.         self.networkConnections = {} # Network connections keyed by file descriptor
  416.         self.__game = None # The game in progress
  417.        
  418.         self.ui = UI(self)
  419.         
  420.         self.history = history.GameHistory()
  421.         
  422.         # Translators: Name of the log that displays application events
  423.         title = _('Application Log')
  424.         self.logger = self.ui.controller.addLogWindow(title, '', '')
  425.  
  426.     def addAIProfile(self, profile):
  427.         """Add a new AI profile into glChess.
  428.         
  429.         'profile' is the profile to add (ai.Profile).
  430.         """
  431.         name = profile.name
  432.         assert(self.__aiProfiles.has_key(name) is False)
  433.         self.__aiProfiles[name] = profile
  434.         self.ui.controller.addAIEngine(name)
  435.  
  436.     def getAIProfile(self, name):
  437.         """Get an installed AI profile.
  438.         
  439.         'name' is the name of the profile to get (string).
  440.         
  441.         Return the profile (ai.Profile) or None if it does not exist.
  442.         """
  443.         try:
  444.             return self.__aiProfiles[name]
  445.         except KeyError:
  446.             return None
  447.         
  448.     def watchAIPlayer(self, p):
  449.         """
  450.         """
  451.         fd = p.fileno()
  452.         if fd is not None:
  453.             self.ioHandlers[fd] = p
  454.             self.ui.controller.watchFileDescriptor(fd)
  455.  
  456.     def unwatchAIPlayer(self, p):
  457.         """
  458.         """
  459.         fd = p.fileno()
  460.         if fd is not None:
  461.             self.ioHandlers.pop(fd)
  462.             
  463.     def addGame(self, name):
  464.         if self.__game is not None:
  465.             # Save the current game to the history
  466.             if self.__game.inHistory:
  467.                 response = ui.SAVE_YES
  468.             elif self.__game.needsSaving:
  469.                 response = self.ui.controller.requestSave('Save current game?')
  470.             else:
  471.                 response = ui.SAVE_NO
  472.  
  473.             if response is ui.SAVE_YES:
  474.                 self.__game.save()
  475.             elif response is ui.SAVE_ABORT:
  476.                 return None
  477.         self.__game = ChessGame(self, name)
  478.         return self.__game
  479.  
  480.     def addLocalGame(self, name, whiteName, whiteType, blackName, blackType):
  481.         """Add a chess game into glChess.
  482.         
  483.         'name' is the name of the game (string).
  484.         'whiteName' is the name of the white player (string).
  485.         'whiteType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players.
  486.         'blackName' is the name of the black player (string).
  487.         'blackType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players.
  488.         
  489.         Returns the game object. Use game.start() to start the game.
  490.         """
  491.         # FIXME: Replace arguments with player objects
  492.         
  493.         # Create the game
  494.         g = self.addGame(name)
  495.         if g is None:
  496.             return None
  497.  
  498.         msg = ''
  499.         if whiteType is None:
  500.             p = g.addHumanPlayer(whiteName)
  501.         else:
  502.             (profile, level) = whiteType
  503.             p = g.addAIPlayer(whiteName, self.__aiProfiles[profile], level)
  504.         g.setWhite(p)
  505.  
  506.         if blackType is None:
  507.             p = g.addHumanPlayer(blackName)
  508.         else:
  509.             (profile, level) = blackType
  510.             p = g.addAIPlayer(blackName, self.__aiProfiles[profile], level)
  511.         g.setBlack(p)
  512.  
  513.         return g
  514.     
  515.     def addPGNGame(self, pgnGame, path, configure = False):
  516.         """Add a PGN game.
  517.         
  518.         'pgnGame' is the game to add (chess.pgn.PGNGame).
  519.         'path' is the path this game was loaded from (string or None).
  520.         
  521.         Returns the game object. Use game.start() to start the game.
  522.         """
  523.         gameProperties = ui.Game()
  524.  
  525.         gameProperties.path = path
  526.         gameProperties.name = pgnGame.getTag(chess.pgn.TAG_EVENT)
  527.         gameProperties.white.name = pgnGame.getTag(chess.pgn.TAG_WHITE)
  528.         gameProperties.black.name = pgnGame.getTag(chess.pgn.TAG_BLACK)
  529.         moves = []
  530.         for pgnMove in pgnGame.getMoves():
  531.             moves.append(pgnMove.move)
  532.         gameProperties.moves = moves            
  533.  
  534.         missingEngines = False
  535.         gameProperties.white.type = pgnGame.getTag('WhiteAI', '')
  536.         if gameProperties.white.type == '':
  537.             w = None
  538.         else:
  539.             if not self.__aiProfiles.has_key(gameProperties.white.type):
  540.                 missingEngines = True
  541.             gameProperties.white.level = pgnGame.getTag('WhiteLevel')
  542.             if gameProperties.white.level is None:
  543.                 gameProperties.white.level = 'normal'
  544.             w = (gameProperties.white.type, gameProperties.white.level)
  545.  
  546.         gameProperties.black.type = pgnGame.getTag('BlackAI', '')
  547.         if gameProperties.black.type == '':
  548.             b = None
  549.         else:
  550.             if not self.__aiProfiles.has_key(gameProperties.black.type):
  551.                 missingEngines = True
  552.             gameProperties.black.level = pgnGame.getTag('BlackLevel')
  553.             if gameProperties.black.level is None:
  554.                 gameProperties.black.level = 'normal'
  555.             b = (gameProperties.black.type, gameProperties.black.level)
  556.  
  557.         # If some of the properties were invalid display the new game dialog
  558.         if missingEngines or configure:
  559.             self.ui.controller.reportGameLoaded(gameProperties)
  560.             return
  561.  
  562.         newGame = self.addLocalGame(gameProperties.name, gameProperties.white.name, w, gameProperties.black.name, b)
  563.         if newGame is None:
  564.             return None
  565.         newGame.date = pgnGame.getTag(chess.pgn.TAG_DATE)
  566.         newGame.fileName = path
  567.         if gameProperties.moves:
  568.             newGame.start(gameProperties.moves)
  569.         else:
  570.             newGame.start()
  571.             
  572.         # Comment on each move
  573.         # FIXME: This should be done through a method so the UI can update better
  574.         moves = newGame.getMoves()
  575.         pgnMoves = pgnGame.getMoves()
  576.         for i in xrange(len(moves)):
  577.             moves[i].comment = pgnMoves[i].comment
  578.             moves[i].nag = pgnMoves[i].nag
  579.  
  580.         # Get the last player to resign if the file specifies it
  581.         result = pgnGame.getTag(chess.pgn.TAG_RESULT, None)
  582.         loser = None
  583.         if result == chess.pgn.RESULT_DRAW:
  584.             newGame.claimDraw()
  585.             if newGame.result != game.RESULT_DRAW:
  586.                 newGame.endGame(game.RESULT_DRAW, game.RULE_AGREEMENT)
  587.         elif result == chess.pgn.RESULT_INCOMPLETE:
  588.             if newGame.result != game.RESULT_IN_PROGRESS:
  589.                 print "WARNING: PGN file specifies game in progress, glChess does't..."            
  590.         elif result == chess.pgn.RESULT_WHITE_WIN:
  591.             loser = newGame.getBlack()
  592.         elif result == chess.pgn.RESULT_BLACK_WIN:
  593.             loser = newGame.getWhite()
  594.         if newGame.result == game.RESULT_IN_PROGRESS and loser is not None:
  595.             loser.resign()
  596.  
  597.         duration = 0
  598.         value = pgnGame.getTag(chess.pgn.TAG_TIME_CONTROL)
  599.         if value is not None:
  600.             timers = value.split(':')
  601.             try:
  602.                 duration = int(timers[0])
  603.             except ValueError:
  604.                 print 'Unknown time control: ' + value
  605.                 
  606.         value = pgnGame.getTag('WhiteTime', duration * 1000)
  607.         try:
  608.             whiteTime = int(value)
  609.         except ValueError:
  610.             whiteTime = duration
  611.         value = pgnGame.getTag('BlackTime', duration * 1000)
  612.         try:
  613.             blackTime = int(value)
  614.         except ValuError:
  615.             blackTime = duration
  616.         newGame.setTimer(duration, whiteTime / 1000, blackTime / 1000)
  617.         
  618.         # No need to save freshly loaded game
  619.         newGame.setNeedsSaving(False)
  620.  
  621.         return newGame
  622.  
  623.     def start(self):
  624.         """Run glChess.
  625.         
  626.         This method does not return.
  627.         """
  628.         self.logger.addLine('This is glChess %s' % VERSION)
  629.         
  630.         # Load AI profiles
  631.         profiles = ai.loadProfiles()
  632.  
  633.         for p in profiles:
  634.             p.detect()
  635.             if p.path is not None:
  636.                 self.logger.addLine('Detected AI: %s at %s' % (p.name, p.path))
  637.                 self.addAIProfile(p)
  638.  
  639.         nArgs = len(sys.argv)
  640.  
  641.         # Load existing games
  642.         if nArgs == 1:
  643.             self.__autoload()
  644.         
  645.         # Load requested game
  646.         elif nArgs == 2:
  647.             path = sys.argv[1]
  648.             import time
  649.             self.logger.addLine('loading...')
  650.             s = time.time()
  651.             try:
  652.                 p = chess.pgn.PGN(path, 1)
  653.             except chess.pgn.Error, e:
  654.                 # TODO: Pop-up dialog
  655.                 self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e)))
  656.             except IOError, e:
  657.                 self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e)))
  658.             else:
  659.                 # Use the first game
  660.                 if len(p) > 0:
  661.                     g = self.addPGNGame(p[0], path)
  662.             self.logger.addLine('loaded in %f seconds' % (time.time() - s))
  663.  
  664.         else:
  665.             # FIXME: Should be in a dialog
  666.             # Translators: Text displayed on the command-line if an unknown argument is passed
  667.             print _('Usage: %s [game]') % sys.argv[0]
  668.             sys.exit(0)
  669.  
  670.         # Start default game if no game present
  671.         if self.__game is None and len(self.__aiProfiles) > 0:
  672.             # Try the previously used AIs first, then any other AI on easy mode
  673.             possibleAIs = [(config.get('new_game_dialog/black/type'),
  674.                             config.get('new_game_dialog/black/difficulty')),
  675.                            (config.get('new_game_dialog/white/type'),
  676.                             config.get('new_game_dialog/white/difficulty'))]
  677.             for p in profiles:
  678.                 possibleAIs.append((p.name, 'easy'))
  679.             
  680.             for (name, difficulty) in possibleAIs:
  681.                 if self.__aiProfiles.has_key(name):
  682.                     black = (name, difficulty)
  683.                     break
  684.             # Translators: Name of a human versus AI game. The %s is replaced with the name of the AI player
  685.             gameName = _('Human versus %s') % black[0]
  686.             # Translators: Name of white player in a default game
  687.             whiteName =  _('White')
  688.             # Translators: Name of black player in a default game            
  689.             blackName = _('Black')
  690.             g = self.addLocalGame(gameName, whiteName, None, blackName, black)
  691.             g.inHistory = True
  692.             g.start()
  693.  
  694.         # Start UI (does not return)
  695.         try:
  696.             self.ui.controller.run()
  697.         except:
  698.             # FIXME: Isn't this done by bug-buddy?
  699.             print _("""glChess has crashed. Please report this bug to http://bugzilla.gnome.org
  700. Debug output:""")
  701.             print traceback.format_exc()
  702.             self.quit()
  703.             sys.exit(1)
  704.         
  705.     def animate(self, timeStep):
  706.         """
  707.         """
  708.         return self.__game.animate(timeStep)
  709.  
  710.     def quit(self):
  711.         """Quit glChess"""
  712.         if self.__game is not None:
  713.             if self.__game.inHistory:
  714.                 response = ui.SAVE_YES
  715.             elif self.__game.needsSaving:
  716.                 response = self.ui.controller.requestSave(_('Save game before closing?'))
  717.             else:
  718.                 response = ui.SAVE_NO
  719.  
  720.             if response == ui.SAVE_YES:
  721.                 self.__game.save()
  722.             elif response == ui.SAVE_ABORT:
  723.                 return
  724.  
  725.             # Abort current game (will delete AIs etc)
  726.             self.__game.abort()
  727.  
  728.         # Notify the UI
  729.         self.ui.controller.close()
  730.  
  731.         # Exit the application
  732.         sys.exit()
  733.         
  734.     # Private methods
  735.  
  736.     def __autoload(self):
  737.         """Restore games from the autosave file"""
  738.         (pgnGame, fileName, inHistory) = self.history.getUnfinishedGame()
  739.         if pgnGame is not None:
  740.             g = self.addPGNGame(pgnGame, fileName)
  741.             if g is not None:
  742.                 g.inHistory = inHistory
  743.  
  744. if __name__ == '__main__':
  745.     app = Application()
  746.     app.start()
  747.